﻿//原先使用System.Web.Caching.Cache，但是asp.net会在System.Web.Caching.Cache缓存页面等数据，
//于是替换了System.Web.Caching.Cache为MemoryCache。
//而在使用MemoryCache的时候，重新启动网站会丢失缓存，于是加了自己的扩展，将缓存序列化存放在文件内，
//在缓存丢的时候从文件内获取缓存，做了简易的扩展。
//总而言之：为了缓存的“健壮性”


using System;
using System.IO;
using System.Runtime.Caching;

namespace Common
{

    public class MemoryCacheHelper
    {
        /// <summary>
        /// 缓存文件根目录
        /// </summary>
        private static string CacheFileRootPath { get; set; }
        /// <summary>
        /// 缓存文件扩展名
        /// </summary>
        private static string CacheFileExt { get; set; }

        static MemoryCacheHelper()
        {
            InitializeStaticMember();
        }

        #region 初始化静态成员

        /// <summary>
        /// 初始化静态成员
        /// </summary>
        public static void InitializeStaticMember()
        {
            CacheFileRootPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LocalConfigHelper.GetValue("Cache", "CacheFileRelativePath", "CacheFile"));
            CacheFileExt = LocalConfigHelper.GetValue("Cache", "CacheFileExt", ".cache");
        }

        #endregion

        #region 更新缓存

        /// <summary>
        /// 更新缓存（key即缓存文件名）
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expireTime"></param>
        /// <returns></returns>
        public static bool UpdateCache(string key, object value, DateTime? expireTime = null)
        {
            bool result = false;

            string cacheFilePath = Path.Combine(CacheFileRootPath, $"{key}{CacheFileExt}");
            CacheItemPolicy policy = new CacheItemPolicy()
            {
                Priority = CacheItemPriority.NotRemovable,
                AbsoluteExpiration = expireTime ?? ObjectCache.InfiniteAbsoluteExpiration,
                RemovedCallback = a => File.Delete(cacheFilePath)//同步线程删除，会等待删除任务完成
            };
            Cache cache = new Cache()
            {
                Value = value,
                FailureTime = policy.AbsoluteExpiration.DateTime
            };
            result = UpdateCache(key, cache, policy, cacheFilePath);

            return result;
        }
        #endregion

        #region 删除缓存

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool RemoveCache(string key)
        {
            MemoryCache.Default.Remove(key);
            return true;
        }

        /// <summary>
        /// 移除全部缓存
        /// </summary>
        /// <returns></returns>
        public static bool RemoveCache()
        {
            foreach (var item in MemoryCache.Default)
            {
                MemoryCache.Default.Remove(item.Key);
            }
            //if (Directory.Exists(fileRootPath))
            //{
            //    Directory.Delete(fileRootPath, true);
            //    Directory.CreateDirectory(fileRootPath);
            //}
            return true;
        }

        #endregion

        #region 查询缓存

        /// <summary>
        /// 获取数据缓存
        /// </summary>
        /// <param name="key">键</param>
        public static T GetCache<T>(string key) where T : class
        {
            T result = null;

            CacheItem cacheItem = MemoryCache.Default.GetCacheItem(key);
            if (cacheItem == null)
            {
                string cacheFilePath = Path.Combine(CacheFileRootPath, $"{key}{CacheFileExt}");
                if (File.Exists(cacheFilePath))
                {
                    #region 复原缓存
                    FileStream cacheFile = File.OpenRead(cacheFilePath);
                    if (cacheFile.CanRead)
                    {
                        Cache cache = SerializeHelper.BinaryDeSerialize(cacheFile) as Cache;
                        if (cache != null)
                        {
                            //缓存未过期
                            if (cache.FailureTime >= DateTime.Now)
                            {
                                CacheItem cacheItemRestore = new CacheItem(key, cache);
                                CacheItemPolicy policy = new CacheItemPolicy()
                                {
                                    Priority = CacheItemPriority.NotRemovable,
                                    AbsoluteExpiration = cache.FailureTime,
                                    RemovedCallback = a => File.Delete(cacheFilePath)//同步线程删除，会等待删除任务完成
                                };
                                MemoryCache.Default.Add(cacheItemRestore, policy);
                                result = cache.Value as T;
                            }
                            else
                            {
                                //缓存过期
                                File.Delete(cacheFilePath);
                                return null;
                            }
                        }
                        else
                        {
                            return null;
                        }
                    }
                    else
                    {
                        return null;
                    }
                    #endregion
                }
                else
                {
                    return null;
                }
            }
            else
            {
                Cache cacheItemValue = cacheItem.Value as Cache;
                if (cacheItemValue != null)
                {
                    result = cacheItemValue.Value as T;
                }
            }

            return result;
        }

        /// <summary>
        /// 获取缓存个数
        /// </summary>
        /// <returns></returns>
        public static int GetCacheCount()
        {
            return (int)MemoryCache.Default.GetCount();
        }

        /// <summary>
        /// 获取缓存文件个数
        /// </summary>
        /// <returns></returns>
        public static object GetCacheFileCount()
        {
            DirectoryInfo di = new DirectoryInfo(CacheFileRootPath);
            return di.GetFiles().Length;
        }

        /// <summary>
        /// 是否存在对应key值的缓存
        /// </summary>
        public static bool IsExistCache(string key)
        {
            return MemoryCache.Default.Get(key) != null;
        }

        #endregion

        #region 刷新缓存
        /// <summary>
        /// 刷新缓存
        /// 主要目的是通过刷新缓存文件，防止因为重启服务导致的缓存文件堆积。
        /// </summary>
        public static void RefreshCache()
        {
            if (Directory.Exists(CacheFileRootPath))
            {
                Directory.CreateDirectory(CacheFileRootPath);
            }
            else
            {
                string[] cacheFilePaths = Directory.GetFiles(CacheFileRootPath, "*{CacheFileExt}");
                foreach (var cacheFilePath in cacheFilePaths)
                {
                    if (File.Exists(cacheFilePath))
                    {
                        #region 复原缓存
                        FileStream cacheFile = File.OpenRead(cacheFilePath);
                        if (cacheFile.CanRead)
                        {
                            Cache cache = SerializeHelper.BinaryDeSerialize(cacheFile) as Cache;
                            if (cache != null)
                            {
                                //缓存未过期
                                if (cache.FailureTime >= DateTime.Now)
                                {
                                    CacheItem cacheItemRestore = new CacheItem(Path.GetFileNameWithoutExtension(cacheFilePath), cache);
                                    CacheItemPolicy policy = new CacheItemPolicy()
                                    {
                                        Priority = CacheItemPriority.NotRemovable,
                                        AbsoluteExpiration = cache.FailureTime,
                                        RemovedCallback = a => File.Delete(cacheFilePath)//同步线程删除，会等待删除任务完成
                                    };
                                    MemoryCache.Default.Add(cacheItemRestore, policy);
                                }
                                else
                                {
                                    //缓存过期
                                    File.Delete(cacheFilePath);
                                }
                            }
                        }
                        #endregion
                    }
                }
            }
        }
        #endregion

        #region 私有方法
        /// <summary>
        /// 更新缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="policy"></param>
        /// <param name="cacheFilePath"></param>
        /// <returns></returns>
        private static bool UpdateCache(string key, object value, CacheItemPolicy policy, string cacheFilePath = null)
        {
            bool result = false;

            if (!string.IsNullOrEmpty(cacheFilePath))
            {
                object existValue = MemoryCache.Default.Get(key);
                //存在则更新
                if (existValue != null)
                {
                    CacheToCacheFile(value, cacheFilePath);
                    existValue = value;
                    result = true;
                }
                else
                {
                    CacheToCacheFile(value, cacheFilePath);
                    result = MemoryCache.Default.Add(new CacheItem(key, value), policy);
                }
            }
            else
            {
                object existValue = MemoryCache.Default.Get(key);
                //存在则更新
                if (existValue != null)
                {
                    existValue = value;
                    result = true;
                }
                else
                {
                    result = MemoryCache.Default.Add(new CacheItem(key, value), policy);
                }
            }

            return result;
        }

        /// <summary>
        /// 同步缓存到缓存文件（缓存文件补丁，防止应用程序重启导致的缓存丢失）
        /// </summary>
        /// <param name="value"></param>
        /// <param name="filepath"></param>
        public static void CacheToCacheFile(object value, string filepath)
        {
            FileStream stream = null;

            if (Directory.Exists(CacheFileRootPath) == false)
            {
                Directory.CreateDirectory(CacheFileRootPath);
            }
            if (File.Exists(filepath) == false)
            {
                stream = new FileStream(filepath, FileMode.CreateNew, FileAccess.Write, FileShare.Write);//创建对象的同时，也就创建了文件
            }
            else
            {
                stream = new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.Write);
            }
            SerializeHelper.BinarySerialize(stream, value);//序列化到缓存文件
        }
        #endregion

        #region 私有类
        /// <summary>
        /// 缓存类（缓存文件补丁，防止应用程序重启导致的缓存丢失）
        /// </summary>
        [Serializable]
        private class Cache
        {
            public object Value { get; set; }
            public DateTime FailureTime { get; set; }
        }
        #endregion
    }

}
